#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>

#include "filedb.h"

//#define DEBUG_FILEDB

// максимальный объем буфера структур типа pt_t
#define MAX_NPT 5000000

//----------------------------------------------------------------------------
// check first and last points in the given file
//----------------------------------------------------------------------------
int file_touch (int fd, pt_t *beg_pt, pt_t *end_pt)
{
	lseek (fd, 0, SEEK_SET);
	read (fd, beg_pt, sizeof (pt_t));
	lseek (fd, -sizeof (pt_t), SEEK_END);
	read (fd, end_pt, sizeof (pt_t));
	//printf ("fd = %d, beg_pt.t = %f, end_pt.t = %f\n", fd, beg_pt->t, end_pt->t);
	
	return 0;
}

//----------------------------------------------------------------------------
// формирует имя файла
//----------------------------------------------------------------------------
char *new_fname (char *buf, char *base_name, char *base_ext, time_t time)
{
	char date [10];
	
	strftime (date, 10, "%Y%m%d", localtime (&time));
	sprintf (buf, "%s.%s.%s", base_name, date, base_ext);

	return buf;
}

//----------------------------------------------------------------------------
// определяет наибольшее id (или дату) файла в каталоге
//----------------------------------------------------------------------------
int last_fname_id (char *base_dir, char *base_name)
{
	DIR *dirp;
	struct dirent *direntp;
	char buf [10];
	int id;	
	int max_id = 0;
	int base_len = strlen (base_name);

	dirp = opendir (base_dir);
	if (dirp != NULL) {
		for (;;) {
            direntp = readdir (dirp);
            if (direntp == NULL) break;
			if (!strncmp (direntp->d_name, base_name, base_len)) {
				if ((strlen (direntp->d_name) + 9) > base_len) {
					memcpy (buf, &direntp->d_name [base_len + 1], 8);
					buf [9] = '\0';
					id = atoi (buf);
					if (id > max_id) max_id = id;
				}
			}
        }
        closedir (dirp);        
    }        
    return max_id;
}

//----------------------------------------------------------------------------
// delete old files with (end_pt->t) < t
//----------------------------------------------------------------------------
int delete_old_files (filedb_t *filedb, double t)
{
	file_t *cur;
	file_t *next;
	char str [512];
	
	cur = filedb->first;
	while (cur) {
		next = cur->next;
		if (cur->end_pt.t < t) {
			if (next) next->prev = NULL;
			if (filedb->first == cur) filedb->first = next;
			if (filedb->last  == cur) filedb->last  = NULL;
			sprintf (str, "%s/%s", filedb->base_dir, cur->name);
		    if (remove (str)) {
        		printf ("Error removing %s\n", str);
        		return (-1);
		    }			
			free (cur);
			cur = NULL;
		}
		cur = next;
	}
	
	return 0;
}

//----------------------------------------------------------------------------
// build buffer with values from files 
// with [(pre)& (t_beg <= t <= t_end)& (post)]
//----------------------------------------------------------------------------
int query_files (filedb_t *filedb, double t_beg, double t_end, pt_t **pt_buf, int *size)
{
	file_t *cur;
	pt_t *out;
	int out_size;
	pt_t buf [1000];
	int i;
	int beg_found;
	int end_found;
	int fd;
	int ret, ret_size;
	char str [512];
	
	out = NULL;
	end_found = 0;
	beg_found = 0;
	out_size = 0;

	cur = filedb->first;
	while (cur) {
		#ifdef DEBUG_FILEDB
		printf ("filename = %s (t_beg = %f, t_end = %f)\n", cur->name, cur->t_beg, cur->t_end);
		#endif
		
		if ((cur->beg_pt.t > 0) && (cur->end_pt.t > 0)) {
			// если в текущем файле нет данных для выбранного отрезка времени, 
			// то считать последнее значение из файла
			if (cur->end_pt.t < t_beg) {
				out = realloc (out, sizeof (pt_t));
				out_size = 1;
				out [0] = cur->end_pt;
				#ifdef DEBUG_FILEDB
				printf ("out_size = %d (out [0].t = %f)\n", out_size, out [0].t); 
				#endif
			}
			
			// если в текущем файле есть необходимые данные 
			if (cur->end_pt.t >= t_beg) {
				#ifdef DEBUG_FILEDB
				printf ("filename = %s (file->t_end >= t_beg)\n", cur->name);
				#endif
				sprintf (str, "%s/%s", filedb->base_dir, cur->name);
				fd = open (str, O_RDONLY);
				lseek (fd, 0, SEEK_SET);
				while (((ret = read (fd, buf, sizeof (buf))) > 0) && (end_found == 0) && (out_size < MAX_NPT)) {
					ret_size = ret / sizeof (*buf);
					if (buf [ret_size - 1].t < t_beg) {
						out = realloc (out, sizeof (pt_t));
						out_size = 1;
						out [0] = buf [ret_size - 1];
					
					} else if (buf [0].t < t_beg) {
						for (i= 0; i< ret_size; i++) {
							if (buf [i].t >= t_beg) {
								beg_found = 1;
								break;	
							}	
						}	
						if (i > 0) {
							out_size += (ret_size - (i-1));
							out = realloc (out, out_size * sizeof (pt_t));
							if (out == NULL) {
								fprintf (stderr, "can't realloc (3) in query_files\n");
								exit (EXIT_FAILURE);
							}
							memcpy (&out [out_size - (ret_size - (i-1))], &buf [i-1], (ret_size - (i-1)) * sizeof (pt_t));
						} else {
							out_size += (ret_size - i);
							out = realloc (out, out_size * sizeof (pt_t));
							if (out == NULL) {
								fprintf (stderr, "can't realloc (4) in query_files\n");
								exit (EXIT_FAILURE);
							}
							memcpy (&out [out_size - (ret_size - i)], &buf [i], (ret_size - i) * sizeof (pt_t));
						}
						#ifdef DEBUG_FILEDB
						printf ("case 2 - ret_size = %d, i= %d, out_size = %d\n", ret_size, i, out_size);
						#endif
						
					} else if (buf [ret_size - 1].t <= t_end) {
						out_size += ret_size;
						out = realloc (out, out_size * sizeof (pt_t));
						if (out == NULL) {
							fprintf (stderr, "can't realloc (1) in query_files\n");
							exit (EXIT_FAILURE);
						}
						memcpy (&out [out_size - ret_size], buf, ret_size * sizeof (pt_t));						
						#ifdef DEBUG_FILEDB
						//printf ("ret_size = %d, out_size = %d\n", ret_size, out_size);
						#endif
					
					} else {
						for (i= 0; i< ret_size; i++) {
							if (buf [i].t > t_end) {
								end_found = 1;
								break;
							}	
						}	
						i++;
						out_size += i;
						out = realloc (out, out_size * sizeof (pt_t));
						if (out == NULL) {
							fprintf (stderr, "can't realloc (2) in query_files\n");
							exit (EXIT_FAILURE);
						}
						memcpy (&out [out_size - i], buf, i * sizeof (pt_t));
						#ifdef DEBUG_FILEDB
						printf ("case 4 - ret_size = %d, i= %d, out_size = %d\n", ret_size, i, out_size);
						#endif
					
					}
				}
			    if (ret == -1) perror ("Error reading file ");
				close (fd);
			}
		}
		cur = cur->next;
	}

	#ifdef DEBUG_FILEDB
	printf ("out_size = %d points\n", out_size); 
	if (out_size > 0) printf ("out->t_beg = %f (t_beg = %f)\n", out [0].t, t_beg); 
	if (out_size > 1) printf ("out->t_end = %f (t_end = %f)\n", out [out_size -1].t, t_end); 
	#endif

	*pt_buf = out;
	*size = out_size;
	return end_found;
}

//----------------------------------------------------------------------------
// enumerate files with name "base_name*" in "base_dir" directory
//----------------------------------------------------------------------------
int enum_files (filedb_t *filedb)
{
	DIR *dirp;
	struct dirent *direntp;
	char buf [512];
	int base_len = strlen (filedb->base_name);
	file_t *file, *cur;
	int fd;
	
	
	dirp = opendir (filedb->base_dir);
	if (dirp != NULL) {
		for (;;) {
            direntp = readdir (dirp);
            if (direntp == NULL) break;
			if (!strncmp (direntp->d_name, filedb->base_name, base_len)) {
				file = malloc (sizeof (file_t));
				memset (file, 0, sizeof (file_t));
				if (file) {
					file->name = strdup (direntp->d_name);
					sprintf (buf, "%s/%s", filedb->base_dir, file->name);
					fd = open (buf, O_RDONLY);
					file_touch (fd, &file->beg_pt, &file->end_pt);
					close (fd);
					if (file->end_pt.t <= file->beg_pt.t) {
						free (file->name);
						free (file);
						file = NULL;
					} else {
						if (filedb->last == NULL) {
							filedb->first = file;
							filedb->last  = file;
						} else {
							if (filedb->last->beg_pt.t <= file->beg_pt.t) {
								filedb->last->next = file;
								file->prev = filedb->last;
								filedb->last = file;
							} else {
								cur = filedb->last;
								while (cur->prev) {
									cur = cur->prev;
									if (cur->beg_pt.t >= file->beg_pt.t) break;
								}
								if (cur->prev) {
									cur->prev->next = file;
								} else {
									filedb->first = file;
								}
								cur->prev = file;
								file->next = cur;
							}
						}
					}
				}
			}
        }
        closedir (dirp);  
    }
	
	return 0;	
}

//----------------------------------------------------------------------------
// очистка сруктуры file_t
//----------------------------------------------------------------------------
int free_file (file_t *file)
{
	free (file->name);
	file->name = NULL;
	return 0;
}

//----------------------------------------------------------------------------
// очистка структуры filedb_t
//----------------------------------------------------------------------------
int free_enum_files (filedb_t *filedb)
{
	file_t *cur;
	
	if (filedb->first) {
		cur = filedb->first;
		do {
			free_file (cur);			
			cur = cur->next;
			if (cur) {
				free (cur->prev);
				cur->prev = NULL;	
			}
		} while (cur);		
		filedb->first = NULL;
		free (filedb->last);
		filedb->last  = NULL;		
	}
	return 0;
}

//----------------------------------------------------------------------------
// отобразить все найденные и опознанные файлы
//----------------------------------------------------------------------------
int print_enum_files (filedb_t *filedb)
{
	file_t *cur;
	
	if (filedb->first) {
		cur = filedb->first;
		do {
			printf ("filename = %s (t_beg = %f, t_end = %f)\n", cur->name, cur->beg_pt.t, cur->end_pt.t);
			cur = cur->next;
		} while (cur);		
	}
	return 0;
}
